// Copyright(c) Sonosemi Medical Ltd. Embedded Software Team.
// Author: Zhang Yisong(zhangyisong@sonosemi.com)

#pragma once

#include "etl/queue.h"
#include "internal/active_policy.hpp"
#include "internal/active_service.hpp"
#include "internal/active_task.hpp"
#include "internal/concepts/active_event.hpp"
#include "internal/concepts/active_policy.hpp"
#include "internal/concepts/active_sm_config.hpp"
#include "internal/concepts/active_task_config.hpp"
#include "active/active_errors.hpp"
#include <variant>

namespace active {

namespace internal {
template <concepts::SmConfigurable TSmConfig, concepts::Policable TPolicy,
          concepts::TaskConfigable TTaskConfig>
struct Active {
  using SmConfigType = TSmConfig;
  using PolicyType = TPolicy;
  using TaskConfigType = TTaskConfig;
  using Self = Active<TSmConfig, TPolicy, TTaskConfig>;
  using StatemachineType = internal::StateMachineType<TSmConfig, TPolicy>;
  using EventsList =
      boost::mp11::mp_filter<is_event_type,
                             decltype(typename StatemachineType::events{})>;
  using ErrorList = boost::mp11::mp_filter<is_error_type, EventsList>;
  using StateMachineSingleton = etl::singleton<StatemachineType>;
  using PolicyList = internal::PolicyList<TPolicy>;
  template <typename T>
  using HasLoggingPolicy =
      std::is_same<T, boost::sml::logger<active::internal::Logger>>;
  static constexpr auto kQueueSize = TTaskConfig::kQueueSize;
  using ProcessingTask =
      std::conditional_t<(kQueueSize > 0),
                         TaskDef<Self, kQueueSize, TTaskConfig::kPriority>,
                         TaskVoid>;

  template <template <typename...> typename TList, typename... TEvent>
  static constexpr auto GetEventQueueType(TList<TEvent...>) {
    if constexpr (kQueueSize > 0) {
      return etl::queue<std::variant<TEvent...>, kQueueSize>{};
    } else {
      return TaskVoid{};
    }
  }
  using EventQueueType =
      std::conditional_t<(kQueueSize > 0),
                         decltype(GetEventQueueType(EventsList{})), TaskVoid>;

  inline static EventQueueType m_eq{};
  static auto Submit(auto event) {
    if (not m_eq.full()) {
      m_eq.emplace(event);
      if (m_eq.size() == 1) {
        cib::CallService<active::service::QueuedEvent>();
      }
    } else {
      // active::Publish("event_queue_is_full"_err);
    }
  }
  template <typename TEvent> struct Export {
    static constexpr auto config = cib::config(
        TEvent::config,
        cib::extend<typename TEvent::Service>(
            [](typename TEvent::DataType data) {
              if constexpr (0 == kQueueSize) {
                StateMachineSingleton::instance().process_event(TEvent{data});
              } else {
                Self::Submit(TEvent{data});
              }
            }));
  };
  template <typename... TEvent> struct Features {
    static constexpr auto config = cib::config(Export<TEvent>::config...);
  };

  template <template <typename...> typename TList, typename... TEvent>
  static constexpr auto MakeFeatures(TList<TEvent...> arg) {
    return Features<TEvent...>{};
  }
  using FeatureConfig = decltype(MakeFeatures(EventsList{}));
  constexpr Active() = default;
  static auto Init() {
    using namespace boost::mp11;

    if constexpr (mp_find_if<PolicyList, HasLoggingPolicy>::value !=
                  mp_size<PolicyList>::value) {
      static internal::Logger logger;
      StateMachineSingleton::create(logger);
    } else {
      StateMachineSingleton::create();
    }
  }
  template <typename TEvent> static auto CheckEvent(auto queued) {
    if (auto ret = std::get_if<TEvent>(&queued); ret != nullptr) {
      StateMachineSingleton::instance().process_event(*ret);
    }
  }
  template <typename TEvent> static auto InitEvent() {
    if constexpr (concepts::EventTyable<TEvent>) {
      TEvent::Service::Init();
    }
  }
  static auto InitEvents() {
    auto f = []<template <typename...> typename TList, typename... TEvent>(
                 TList<TEvent...>) { (InitEvent<TEvent>(), ...); };
    f(EventsList{});
  }
  static auto ProcessingWork() {
    if constexpr (not internal::is_task_void<EventQueueType>) {
      auto handle_matched_event =
          []<template <typename...> typename TList, typename... TEvent>(
              TList<TEvent...>, auto evt) { (CheckEvent<TEvent>(evt), ...); };
      auto event = m_eq.front();
      handle_matched_event(EventsList{}, event);
      m_eq.pop();
    }
  }
  static auto QueueEmpty() { return m_eq.empty(); }
  static auto QueueSize() { return m_eq.size(); }
};

} // namespace internal

} // namespace active